﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;

namespace DTW_Chart
{
    class DTW
    {
        /// <summary>
        /// Print out the Matching Points 
        /// </summary>
        /// <param name="result">List of Matching Points</param>
        public void printOutResult(List<MatchingPoints> result)
        {
            for (int i = 0; i < result.Count; i++)
            {
                Console.WriteLine("(" + result[i].first.X + "," + result[i].first.Y + ") -- (" + result[i].second.X + "," + result[i].second.Y + ")");
            }
        }
        /// <summary>
        /// Implement the classic DTW algorithm without Variation
        /// </summary>
        /// <param name="a">first Sequence</param>
        /// <param name="b">second Sequence</param>
        /// <param name="distance">The total distance of Warp sequence</param>
        /// <returns></returns>
        public List<MatchingPoints> DynamicTimeWarpingClassic(Sequence a, Sequence b, ref double distance)
        {
            Calculator calculate = new Calculator();
            //  Translate matrix a to b
            calculate.translateSequence(a, b);

            List<PointD> firstSeq = a.getSequence();
            List<PointD> secondSeq = b.getSequence();

            List<MatchingPoints> result = new List<MatchingPoints>();

            //  Make cost matrix
            double[,] costMatrix = calculate.makeCostMatrixClassic(firstSeq, secondSeq);
            calculate.printOutCostMatrix(costMatrix);

            //  Find the lowest cost path
            int n = firstSeq.Count - 1;
            int m = secondSeq.Count - 1;
            result.Add(new MatchingPoints(firstSeq[0], secondSeq[0]));
            while (n > 0 && m > 0)
            {
                
                if (n == 0)
                {
                    m--;
                }
                else if (m == 0)
                {
                    n--;
                }
                else
                {
                    double min = calculate.findMin(costMatrix[n, m - 1], costMatrix[n - 1, m], costMatrix[n - 1, m - 1]);
                    //  Console.WriteLine("Min = " + min);
                    if (min == costMatrix[n, m - 1])
                    {
                        m--;
                    }
                    else if (min == costMatrix[n - 1, m])
                    {
                        n--;
                    }
                    else
                    {
                        n--;
                        m--;
                    }
                }
                //  Console.WriteLine("Cost = " + costMatrix[n, m]);
                distance += costMatrix[n, m];
                result.Add(new MatchingPoints(firstSeq[n], secondSeq[m]));
            }

            return result;
        }
        /// <summary>
        /// Implement DTW algorithm with Step Size Condition improvement
        /// </summary>
        /// <param name="a">first sequence</param>
        /// <param name="b">second sequence</param>
        /// <param name="distance">total cost of warp sequence</param>
        /// <returns></returns>
        public List<MatchingPoints> DynamicTimeWarpingStepSizeCondition(Sequence a, Sequence b, ref double distance) 
        {
            Calculator calculate = new Calculator();
            //  Translate matrix a to b
            calculate.translateSequence(a, b);

            List<PointD> firstSeq = a.getSequence();
            List<PointD> secondSeq = b.getSequence();

            List<MatchingPoints> result = new List<MatchingPoints>();

            //  Make cost matrix
            //  Improvement here
            //  Step Size
            double[,] costMatrix = calculate.makeCostMatrixStepSize(firstSeq, secondSeq);
            calculate.printOutCostMatrix(costMatrix);

            //  Find the lowest cost path
            int n = firstSeq.Count - 1;
            int m = secondSeq.Count - 1;
            result.Add(new MatchingPoints(firstSeq[0], secondSeq[0]));
            while (n > 0 && m > 0)
            {

                if (n == 0)
                {
                    m--;
                }
                else if (m == 0)
                {
                    n--;
                }
                else
                {
                    double min = calculate.findMin(costMatrix[n, m - 1], costMatrix[n - 1, m], costMatrix[n - 1, m - 1]);
                    //  Console.WriteLine("Min = " + min);
                    if (min == costMatrix[n, m - 1])
                    {
                        m--;
                    }
                    else if (min == costMatrix[n - 1, m])
                    {
                        n--;
                    }
                    else
                    {
                        n--;
                        m--;
                    }
                }
                //  Console.WriteLine("Cost = " + costMatrix[n, m]);
                distance += costMatrix[n, m];
                result.Add(new MatchingPoints(firstSeq[n], secondSeq[m]));
            }

            return result;
        }
        /// <summary>
        /// Implement DTW algorithm with local weight improvement
        /// </summary>
        /// <param name="a">first sequence</param>
        /// <param name="b">second sequence</param>
        /// <param name="distance">total cost of warp sequence</param>
        /// <returns></returns>
        public List<MatchingPoints> DynamicTimeWarpingLocalWeight(Sequence a, Sequence b, ref double distance)
        {
            Calculator calculate = new Calculator();
            //  Translate matrix a to b
            calculate.translateSequence(a, b);

            List<PointD> firstSeq = a.getSequence();
            List<PointD> secondSeq = b.getSequence();

            List<MatchingPoints> result = new List<MatchingPoints>();

            //  Make cost matrix
            //  Improvement here
            //  Local Weight
            double[,] costMatrix = calculate.makeCostMatrixClassic(firstSeq, secondSeq);
            calculate.printOutCostMatrix(costMatrix);

            //  Find the lowest cost path
            int n = firstSeq.Count - 1;
            int m = secondSeq.Count - 1;
            result.Add(new MatchingPoints(firstSeq[0], secondSeq[0]));
            while (n > 0 && m > 0)
            {

                if (n == 0)
                {
                    m--;
                }
                else if (m == 0)
                {
                    n--;
                }
                else
                {
                    double min = calculate.findMin(costMatrix[n, m - 1], costMatrix[n - 1, m], costMatrix[n - 1, m - 1]);
                    //  Console.WriteLine("Min = " + min);
                    if (min == costMatrix[n, m - 1])
                    {
                        m--;
                    }
                    else if (min == costMatrix[n - 1, m])
                    {
                        n--;
                    }
                    else
                    {
                        n--;
                        m--;
                    }
                }
                //  Console.WriteLine("Cost = " + costMatrix[n, m]);
                distance += costMatrix[n, m];
                result.Add(new MatchingPoints(firstSeq[n], secondSeq[m]));
            }

            return result;
        }
        /// <summary>
        /// Implement DTW algorithm with global constraints improvement
        /// </summary>
        /// <param name="a">first sequence</param>
        /// <param name="b">second sequence</param>
        /// <param name="distance">total cost of warp sequence</param>
        /// <returns></returns>
        public List<MatchingPoints> DynamicTimeWarpingGlobalConstraints(Sequence a, Sequence b, ref double distance)
        {
            Calculator calculate = new Calculator();
            //  Translate matrix a to b
            calculate.translateSequence(a, b);

            List<PointD> firstSeq = a.getSequence();
            List<PointD> secondSeq = b.getSequence();

            List<MatchingPoints> result = new List<MatchingPoints>();

            //  Make cost matrix
            double[,] costMatrix = calculate.makeCostMatrixClassic(firstSeq, secondSeq);
            calculate.printOutCostMatrix(costMatrix);

            //  Find the lowest cost path
            int n = firstSeq.Count - 1;
            int m = secondSeq.Count - 1;
            result.Add(new MatchingPoints(firstSeq[0], secondSeq[0]));
            while (n > 0 && m > 0)
            {

                if (n == 0)
                {
                    m--;
                }
                else if (m == 0)
                {
                    n--;
                }
                else
                {
                    //  Improvement here
                    //  TODO: Global Constraints
                    double min = calculate.findMin(costMatrix[n, m - 1], costMatrix[n - 1, m], costMatrix[n - 1, m - 1]);
                    //  Console.WriteLine("Min = " + min);
                    if (min == costMatrix[n, m - 1])
                    {
                        m--;
                    }
                    else if (min == costMatrix[n - 1, m])
                    {
                        n--;
                    }
                    else
                    {
                        n--;
                        m--;
                    }
                }
                //  Console.WriteLine("Cost = " + costMatrix[n, m]);
                distance += costMatrix[n, m];
                result.Add(new MatchingPoints(firstSeq[n], secondSeq[m]));
            }

            return result;
        }
    }
}
